<template>
  <div id="cesiumContainer"></div>
</template>

<script setup>
import {onBeforeUnmount, onMounted, shallowRef} from "vue";
import {Ion, Viewer, Terrain, Entity, Cartesian3, Color, IonResource, Rectangle, LabelStyle, Cartesian2} from 'cesium'
import "cesium/Build/Cesium/Widgets/widgets.css";
import * as Cesium from "cesium";

// 服务器上托管 CesiumJS 静态文件的 URL。
window.CESIUM_BASE_URL = '/Cesium';
// 您的访问令牌可在以下位置找到：https://ion.cesium.com/tokens。
// 将“your_access_token”替换为您的铯离子访问令牌。
Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIyNjJhZWE3NC1iYjY2LTQwZjAtODc0YS0zZTdlMjRmNTE5ZDEiLCJpZCI6MjA1NjY1LCJpYXQiOjE3MTE5MzExMzB9.efKdT634nrROZLOR-GmU5gvPmnlCDsiHDTkCYlU4SXA';
// Blocked script execution in 'about:blank' because the document's frame is sandboxed and the 'allow-scripts' permission is not set.


/* 创建视图（查看器） */
async function createViewer() {
  const viewer = new Viewer('cesiumContainer', {
    animation: false, // 隐藏左下角动画部件
    timeline: false, // 隐藏底部时间轴部件
    fullscreenButton: false, // 隐藏右下角全屏按钮部件
    // creditDisplay: false, // 隐藏屏幕和灯箱中显示的制作人员名单。
    geocoder: false, // 隐藏右上角搜索按钮部件
    homeButton: false, // 隐藏右上角 Home 按钮部件
    sceneModePicker: false, // 隐藏右上角场景模式按钮部件
    baseLayerPicker: false, // 隐藏右上角图层选择按钮部件
    navigationHelpButton: false, // 隐藏右上角帮助按钮部件
    vrButton: true, // 显示右下角 VRButton 按钮部件
    // selectionIndicator: false, // 隐藏点击要素之后显示的选择指示器
    // infoBox: false, // 隐藏点击要素之后显示的信息框
    // skyBox: false, // 天空盒，取消默认天空盒、太阳、月亮
    // skyAtmosphere: false, // 天空大气层，取消大气层效果 蔚蓝的天空，以及地球边缘的光芒
    // globe: false, // 要在场景中使用的地球仪。如果设置为 false ，则不会添加任何地球仪，并且默认情况下将隐藏天空大气层。
    // projectionPicker: true, // 显示右上角投影选择按钮部件
    terrain: Terrain.fromWorldTerrain({
      requestVertexNormals: true, // 请求其他照明信息
      requestWaterMask: true, // 请求每个磁贴的水掩码
    })

  });
  console.log('视图查看器：', viewer);
  // 隐藏底部 Cesium logo 与 attribution 标注信息部件
  viewer.bottomContainer.style.display = 'none'
  return viewer;
}

/* 创建实体 Entity */
async function createEntity() {
  return new Entity({
    show: true, // 是否显示实体及其子实体
    // 此对象的唯一标识符。如果未提供任何信息，则生成 GUID
    // const entity = viewer.entities.getById("uniqueId"); 使用 getById 检索实体。如果不存在具有所提供 ID 的实体，则返回 undefined。
    // const entity = viewer.entities.getOrCreateEntity("uniqueId"); 若要提取实体或创建新实体（如果该实体不存在），请使用 getOrCreateEntity。
    id: '001',
    name: '创建多个实体', // 信息框标题
    description: '信息框的描述信息，可以为<a href="https://www.baidu.com" target="_blank">HTML</a>元素，也可以是字符串', // 指定此实体的 HTML 说明
    position: Cartesian3.fromDegrees(-100.0, 40.0), // 用于指定实体的位置
    // orientation: new Cesium.VelocityOrientationProperty(position), // 用于指定实体的方向
    // 广告牌 - 用于指定实体的图标
    /*billboard: {
      show: true, // 是否显示图片
      image: 'src/assets/avatar.jpg', // 图片路径
      width: 64, // 图片宽度
      height: 64, // 图片高度
      scale: 1, // 图片缩放
      // 偏移量
      // pixelOffset: new Cartesian2(0, -80),
      // 距离显示条件 - 2000 米内显示
      // distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0.0, 2000.0),
    },*/
    // 用于指定实体的标签
    label: {
      text: 'Entity Label',
      font: '16px sans-serif',
      fillColor: Color.WHITE,
      outlineColor: Color.BLACK,
      outlineWidth: 4,
      // 样式 FILL OUTLINE FILL_AND_OUTLINE 默认 FILL
      style: LabelStyle.FILL_AND_OUTLINE,
      // 标签相对于实体的偏移量
      pixelOffset: new Cartesian2(0, -80),
      // 距离显示条件 - 2000 米内显示
      // distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0.0, 2000.0),
    },
    // 点
    point: {
      show: true, // 是否显示点
      pixelSize: 10, // 点大小
      color: Color.BLUE, // 点颜色
    },
    // 线 - 用于指定实体的折线
    polyline: {
      show: true, // 是否显示线
      positions: Cartesian3.fromDegreesArray([
        -104.053011, 41.003906, -100.0, 40.0
      ]), // 线段位置
      width: 5, // 线段宽度
      material: Color.YELLOW, // 线段颜色
    },
    // 多边形（面）
    polygon: {
      hierarchy: Cartesian3.fromDegreesArray([
        -109.080842, 45.002073, -105.91517, 45.002073, -104.058488, 44.996596,
        -104.053011, 43.002989, -104.053011, 41.003906, -109.080842, 45.002073,
      ]),
      height: 0,
      // extrudedHeight: 250000,
      fill: true,
      material: Color.RED.withAlpha(0.5),
      outline: true,
      outlineColor: Color.BLACK,
    },
    // 矩形
    rectangle: {
      coordinates: Rectangle.fromDegrees(-110.0, 40.0, -100.0, 45.0),
      height: 0,
      // extrudedHeight: 250000,
      material: Color.GREEN.withAlpha(0.5),
      outline: true,
      outlineColor: Color.BLACK,
    },
    // 圆
    circle: {
      center: Cartesian3.fromDegrees(-100.0, 45.0), // 圆心
      radius: 50000.0, // 半径
      height: 0,
      // extrudedHeight: 250000,
      material: Color.RED.withAlpha(0.5),
      outline: true,
      outlineColor: Color.BLACK,
    },
    // 椭圆
    ellipse: {
      semiMajorAxis: 100000.0, // 长半轴
      semiMinorAxis: 50000.0, // 短半轴
      height: 0,
      // extrudedHeight: 250000,
      material: Color.BLUE.withAlpha(0.5),
      outline: true,
      outlineColor: Color.BLACK,
    },
    // 模型
    model: {
      uri: await IonResource.fromAssetId(2522523),
      minimumPixelSize: 128,
      maximumScale: 20000,
    }
  })
}

/**
 * 返回所提供窗口坐标处最顶层的实体或未定义（如果该位置没有实体）。
 *
 * @param viewer
 * @param {Cartesian2} windowPosition 窗口坐标。
 * @returns {Entity} 选取的实体或未定义的实体。
 */
function pickEntity(viewer, windowPosition) {
  const picked = viewer.scene.pick(windowPosition);
  if (Cesium.defined(picked)) {
    const id = Cesium.defaultValue(picked.id, picked.primitive.id);
    if (id instanceof Cesium.Entity) {
      return id;
    }
  }
  return undefined;
}

/**
 * 返回提供的窗口坐标处的实体列表。实体按视觉顺序从前到后排序。
 *
 * @param viewer
 * @param {Cartesian2} windowPosition 窗口坐标.
 * @returns {Entity[]} 选取的实体或未定义的实体.
 */
function drillPickEntities(viewer, windowPosition) {
  let picked, entity, i;
  const pickedPrimitives = viewer.scene.drillPick(windowPosition);
  const length = pickedPrimitives.length;
  const result = [];
  const hash = {};

  for (i = 0; i < length; i++) {
    picked = pickedPrimitives[i];
    entity = Cesium.defaultValue(picked.id, picked.primitive.id);
    if (entity instanceof Cesium.Entity && !Cesium.defined(hash[entity.id])) {
      result.push(entity);
      hash[entity.id] = true;
    }
  }
  return result;
}

const cesiumViewer = shallowRef()
const billboardEntity = shallowRef()

// 测试 pick
function testPick(position) {
  const viewer = cesiumViewer.value
  let entity = pickEntity(viewer, position);
  console.log('pickEntity：', entity);
  // 修改实体图片
  if (entity && entity.id === '002') {
    entity.billboard.image = 'src/assets/vue.svg';
  } else {
    // 点击其他地方恢复图片
    billboardEntity.value.billboard.image = 'src/assets/avatar.jpg'
  }
}

// 测试 drillPick
function testDrillPick(position) {
  const viewer = cesiumViewer.value
  let entities = drillPickEntities(viewer, position);
  console.log('drillPickEntities：', entities);
}

onMounted(async () => {
  let viewer = createViewer();
  cesiumViewer.value = viewer
  // 添加实体
  let entity = await createEntity();
  viewer.entities.add(entity);
  // 有时，特别是在处理时间动态数据时，我们希望相机保持以实体为中心，而不是以地球为中心 - 多应用在 3D 模型
  // viewer.trackedEntity = entity;
  // 添加广告牌实体
  const billboard = new Entity({
        id: '002',
        name: '广告牌',
        description: '广告牌描述',
        position: Cartesian3.fromDegrees(-100.0, 40.0), // 用于指定实体的位置
        // 广告牌 - 用于指定实体的图标
        billboard: {
          show: true, // 是否显示图片
          image: 'src/assets/avatar.jpg', // 图片路径
          width: 64, // 图片宽度
          height: 64, // 图片高度
          scale: 1, // 图片缩放
          // 偏移量
          // pixelOffset: new Cartesian2(0, -80),
          // 距离显示条件 - 2000 米内显示
          // distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0.0, 2000.0),
        }
      }
  )
  viewer.entities.add(billboard)
  billboardEntity.value = billboard
  // 缩放(飞行)至实体
  let result = await viewer.zoomTo(entity);
  // let result = await viewer.flyTo(entity);
  // 相机控制
  // const heading = Cesium.Math.toRadians(90);
  // const pitch = Cesium.Math.toRadians(-30);
  // let result = await viewer.zoomTo(entity, new Cesium.HeadingPitchRange(heading, pitch));
  // 选择实体（默认展示 infoBox 信息框）
  if (result) {
    viewer.selectedEntity = entity
  }
  // window.addEventListener('click', testPick)
  // window.addEventListener('click', testDrillPick)
  // 屏幕空间事件处理器
  // https://cesium.com/learn/cesiumjs/ref-doc/ScreenSpaceEventHandler.html?classFilter=ScreenSpaceEventHandler
  const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
  handler.setInputAction(({position}) => {
    console.log('笛卡尔2D坐标系（屏幕坐标）：', position)
    testPick(position)
  }, Cesium.ScreenSpaceEventType.LEFT_CLICK) // 鼠标左键点击
})

onBeforeUnmount(() => {
  // window.removeEventListener('click', testPick)
  // window.removeEventListener('click', testDrillPick)
})
</script>

<style scoped>
#cesiumContainer {
  width: 100%;
  height: 100%;
}
</style>
